﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Web;
using System.Web.Configuration;
using System.Xml;


//[assembly: PreApplicationStartMethod(typeof(AjaxControlFramework.AjaxControlInitialization), "Init")]

using AjaxControlFramework.Compilation;

namespace AjaxControlFramework
{
    public static class AjaxControlInitialization
    {
        //------// Properties \\--------------------------------------------\\
        private static Configuration _webConfig = null;
        private static Configuration WebConfig
        {
            get
            {
                if (_webConfig == null)
                {
                    ConfigurationSection configSection = WebConfigurationManager.GetSection("system.web") as ConfigurationSection;

                    if (configSection == null)
                    {
                        _webConfig = WebConfigurationManager.OpenWebConfiguration("~");
                    }
                    else
                    {
                        _webConfig = configSection.CurrentConfiguration;
                    }
                }

                return _webConfig;
            }
        }


        private static XmlDocument _webConfigDocument = null;
        public static XmlDocument WebConfigDocument
        {
            get
            {
                if (_webConfigDocument == null)
                {
                    _webConfigDocument = new XmlDocument();
                    _webConfigDocument.Load(WebConfigFilePath);
                }

                return _webConfigDocument;
            }
        }


        private static Dictionary<string, List<Configuration>> _nonIntegratedPipelineLocations = null;
        private static Dictionary<string, List<Configuration>> NonIntegratedPipelineLocations
        {
            get
            {
                if (_nonIntegratedPipelineLocations == null)
                {
                    _nonIntegratedPipelineLocations = new Dictionary<string, List<Configuration>>();

                    foreach (ConfigurationLocation location in WebConfig.Locations)
                    {
                        if (_nonIntegratedPipelineLocations.ContainsKey(location.Path))
                        {
                            _nonIntegratedPipelineLocations[location.Path].Add(location.OpenConfiguration());
                        }
                        else
                        {
                            List<Configuration> locationConfigurations = new List<Configuration>();
                            locationConfigurations.Add(location.OpenConfiguration());

                            _nonIntegratedPipelineLocations.Add(location.Path, locationConfigurations);
                        }
                    }
                }

                return _nonIntegratedPipelineLocations;
            }
        }


        private static Dictionary<string, List<XmlNode>> _integratedPipelineLocations = null;
        public static Dictionary<string, List<XmlNode>> IntegratedPipelineLocations
        {
            get
            {
                if (_integratedPipelineLocations == null)
                {
                    _integratedPipelineLocations = new Dictionary<string, List<XmlNode>>();


                    XmlNodeList locationNodes = WebConfigDocument.SelectNodes("/configuration/location");

                    foreach (XmlNode locationNode in locationNodes)
                    {
                        XmlAttribute pathAttribute = locationNode.Attributes["path"];

                        if (pathAttribute == null || String.IsNullOrWhiteSpace(pathAttribute.Value))
                        {
                            continue;
                        }


                        if (_integratedPipelineLocations.ContainsKey(pathAttribute.Value))
                        {
                            _integratedPipelineLocations[pathAttribute.Value].Add(locationNode);
                        }
                        else
                        {
                            List<XmlNode> locations = new List<XmlNode>();
                            locations.Add(locationNode);

                            _integratedPipelineLocations.Add(pathAttribute.Value, locations);
                        }
                    }
                }

                return _integratedPipelineLocations;
            }
        }


        private static List<HttpHandlersSection> _nonIntegratedPipelineHttpHandlers = null;
        private static List<HttpHandlersSection> NonIntegratedPipelineHttpHandlers
        {
            get
            {
                if (_nonIntegratedPipelineHttpHandlers == null)
                {
                    _nonIntegratedPipelineHttpHandlers = new List<HttpHandlersSection>();

                    if (NonIntegratedPipelineLocations.Count > 0)
                    {
                        foreach (List<Configuration> locationConfigurations in NonIntegratedPipelineLocations.Values)
                        {
                            foreach (Configuration configuration in locationConfigurations)
                            {
                                HttpHandlersSection handlersSection = configuration.GetSection("system.web/httpHandlers") as HttpHandlersSection;

                                if (handlersSection != null)
                                {
                                    _nonIntegratedPipelineHttpHandlers.Add(handlersSection);
                                    break;
                                }
                            }
                        }
                    }

                    if (_nonIntegratedPipelineHttpHandlers.Count == 0)
                    {
                        HttpHandlersSection handlersSection = WebConfig.GetSection("system.web/httpHandlers") as HttpHandlersSection;

                        if (handlersSection != null)
                        {
                            _nonIntegratedPipelineHttpHandlers.Add(handlersSection);
                        }
                    }
                }

                return _nonIntegratedPipelineHttpHandlers;
            }
        }


        private static List<BuildProviderCollection> _nonIntegratedPipelineBuildProviders = null;
        private static List<BuildProviderCollection> NonIntegratedPipelineBuildProviders
        {
            get
            {
                if (_nonIntegratedPipelineBuildProviders == null)
                {
                    _nonIntegratedPipelineBuildProviders = new List<BuildProviderCollection>();

                    if (NonIntegratedPipelineLocations.Count > 0)
                    {
                        foreach (List<Configuration> locationConfigurations in NonIntegratedPipelineLocations.Values)
                        {
                            foreach (Configuration configuration in locationConfigurations)
                            {
                                BuildProviderCollection buildProviders = (configuration.GetSection("system.web/compilation") as CompilationSection).BuildProviders;

                                if (buildProviders != null)
                                {
                                    _nonIntegratedPipelineBuildProviders.Add(buildProviders);
                                    break;
                                }
                            }
                        }
                    }

                    if (_nonIntegratedPipelineBuildProviders.Count == 0)
                    {
                        BuildProviderCollection buildProviders = (WebConfig.GetSection("system.web/compilation") as CompilationSection).BuildProviders;

                        if (buildProviders != null)
                        {
                            _nonIntegratedPipelineBuildProviders.Add(buildProviders);
                        }
                    }
                }

                return _nonIntegratedPipelineBuildProviders;
            }
        }


        private static List<AssemblyCollection> _nonIntegratedPipelineAssemblies = null;
        private static List<AssemblyCollection> NonIntegratedPipelineAssemblies
        {
            get
            {
                if (_nonIntegratedPipelineAssemblies == null)
                {
                    _nonIntegratedPipelineAssemblies = new List<AssemblyCollection>();

                    if (NonIntegratedPipelineLocations.Count > 0)
                    {
                        foreach (List<Configuration> locationConfigurations in NonIntegratedPipelineLocations.Values)
                        {
                            foreach (Configuration configuration in locationConfigurations)
                            {
                                AssemblyCollection assemblies = (configuration.GetSection("system.web/compilation") as CompilationSection).Assemblies;

                                if (assemblies != null)
                                {
                                    _nonIntegratedPipelineAssemblies.Add(assemblies);
                                    break;
                                }
                            }
                        }
                    }

                    if (_nonIntegratedPipelineAssemblies.Count == 0)
                    {
                        AssemblyCollection assemblies = (WebConfig.GetSection("system.web/compilation") as CompilationSection).Assemblies;

                        if (assemblies != null)
                        {
                            _nonIntegratedPipelineAssemblies.Add(assemblies);
                        }
                    }
                }

                return _nonIntegratedPipelineAssemblies;
            }
        }


        private static List<NamespaceCollection> _nonIntegratedPipelineNamespaces = null;
        private static List<NamespaceCollection> NonIntegratedPipelineNamespaces
        {
            get
            {
                if (_nonIntegratedPipelineNamespaces == null)
                {
                    _nonIntegratedPipelineNamespaces = new List<NamespaceCollection>();

                    if (NonIntegratedPipelineLocations.Count > 0)
                    {
                        foreach (List<Configuration> locationConfigurations in NonIntegratedPipelineLocations.Values)
                        {
                            foreach (Configuration configuration in locationConfigurations)
                            {
                                NamespaceCollection namespaces = (configuration.GetSection("system.web/pages") as PagesSection).Namespaces;

                                if (namespaces != null)
                                {
                                    _nonIntegratedPipelineNamespaces.Add(namespaces);
                                    break;
                                }
                            }
                        }
                    }

                    if (_nonIntegratedPipelineNamespaces.Count == 0)
                    {
                        NamespaceCollection namespaces = (WebConfig.GetSection("system.web/pages") as PagesSection).Namespaces;

                        if (namespaces != null)
                        {
                            _nonIntegratedPipelineNamespaces.Add(namespaces);
                        }
                    }
                }

                return _nonIntegratedPipelineNamespaces;
            }
        }


        private static List<TagPrefixCollection> _nonIntegratedPipelineControls = null;
        private static List<TagPrefixCollection> NonIntegratedPipelineControls
        {
            get
            {
                if (_nonIntegratedPipelineControls == null)
                {
                    _nonIntegratedPipelineControls = new List<TagPrefixCollection>();

                    if (NonIntegratedPipelineLocations.Count > 0)
                    {
                        foreach (List<Configuration> locationConfigurations in NonIntegratedPipelineLocations.Values)
                        {
                            foreach (Configuration configuration in locationConfigurations)
                            {
                                TagPrefixCollection controls = (configuration.GetSection("system.web/pages") as PagesSection).Controls;

                                if (controls != null)
                                {
                                    _nonIntegratedPipelineControls.Add(controls);
                                    break;
                                }
                            }
                        }
                    }

                    if (_nonIntegratedPipelineControls.Count == 0)
                    {
                        TagPrefixCollection controls = (WebConfig.GetSection("system.web/pages") as PagesSection).Controls;

                        if (controls != null)
                        {
                            _nonIntegratedPipelineControls.Add(controls);
                        }
                    }
                }

                return _nonIntegratedPipelineControls;
            }
        }


        private static string _webConfigFilePath = null;
        private static string WebConfigFilePath
        {
            get
            {
                if (_webConfigFilePath == null)
                {
                    _webConfigFilePath = HttpContext.Current.Server.MapPath("~/web.config");
                }

                return _webConfigFilePath;
            }
        }
        //------\\ Properties //--------------------------------------------//



        //------// Fields \\------------------------------------------------\\
        private static bool WebConfigModified = false;
        //------\\ Fields //------------------------------------------------//



        //------// Methods \\-----------------------------------------------\\
        public static void Init()
        {
            if (WebConfigDocument != null && !WebConfigModified && CanWriteToWebConfig())
            {
                lock (WebConfigDocument)
                {
                    // 1.) Register the AjaxControlHandler HTTP Handler.
                    RegisterAjaxControlHandler();


                    // 2.) Register the JavaScriptBuildProvider Build Provider.
                    RegisterJavaScriptBuilderProvider();


                    // 3.) Register the JsxSriptHandler HTTP Handler.
                    RegisterJsxScriptHandler();


                    // 4.) Register the assemblies declaration.
                    RegisterAssembliesDeclaration();


                    // 5.) Register the pages/namespaces declaration.
                    RegisterNamespaceDeclaration();


                    // 6.) Register the pages/controls declaration (tag prefix).
                    RegisterControlDeclaration();


                    // 7.) Save all the changes to the web.config. This will trigger a restart of the web application, but this shouldn't be 
                    //     noticable at all because this is done before any bit of the HttpApplication is initialized.
                    SaveWebConfig();
                }
            }
        }


        private static bool CanWriteToWebConfig()
        {
            bool allowsWriting = false, deniesWriting = false;

            DirectorySecurity accessControl = Directory.GetAccessControl(WebConfigFilePath);
            System.Security.AccessControl.AuthorizationRuleCollection accessRules = accessControl.GetAccessRules(true, true, typeof(SecurityIdentifier));

            foreach (FileSystemAccessRule accessRule in accessRules)
            {
                if ((FileSystemRights.Write & accessRule.FileSystemRights) != FileSystemRights.Write) { continue; }

                if (accessRule.AccessControlType == AccessControlType.Allow)
                {
                    allowsWriting = true;
                }
                else if (accessRule.AccessControlType == AccessControlType.Deny)
                {
                    deniesWriting = true;
                }
            }

            return allowsWriting && !deniesWriting;
        }


        private static void SaveWebConfig()
        {
            if (WebConfigModified)
            {
                if (!HttpRuntime.UsingIntegratedPipeline)
                {
                    WebConfig.Save();
                    _webConfig = null;
                }
                else
                {
                    WebConfigDocument.Save(WebConfigFilePath);
                    _webConfigDocument = null;
                }
            }
        }


        private static void RegisterAjaxControlHandler()
        {
            string handlerPath = null;
            
            if (HttpRuntime.AppDomainAppVirtualPath == "/")
            {
                handlerPath = "*AjaxControlHandler.ashx";
            }
            else
            {
                handlerPath = (!HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? HttpRuntime.AppDomainAppVirtualPath + "/" : HttpRuntime.AppDomainAppVirtualPath) + "AjaxControlHandler.ashx";

                if (handlerPath.StartsWith("/"))
                {
                    handlerPath = handlerPath.Substring(1);
                }

                handlerPath = "*" + handlerPath;
            }


            if (!HttpRuntime.UsingIntegratedPipeline)
            {
                RegisterNonIntegratedPipelineHttpHandler(typeof(AjaxControlHandler).AssemblyQualifiedName, "POST", handlerPath);
            }
            else
            {
                RegisterIntegratedPipelineHttpHandler(typeof(AjaxControlHandler).AssemblyQualifiedName, "AjaxControlHandler", "POST", handlerPath);
            }
        }


        private static void RegisterJsxScriptHandler()
        {
            if (!HttpRuntime.UsingIntegratedPipeline)
            {
                RegisterNonIntegratedPipelineHttpHandler(typeof(JsxScriptHandler).AssemblyQualifiedName, "GET", "*.jsx");
            }
            else
            {
                RegisterIntegratedPipelineHttpHandler(typeof(JsxScriptHandler).AssemblyQualifiedName, "JsxScriptHandler", "GET", "*.jsx");
            }
        }


        private static void RegisterJavaScriptBuilderProvider()
        {
            if (!HttpRuntime.UsingIntegratedPipeline)
            {
                RegisterNonIntegratedPipelineBuildProvider(typeof(AjaxControlFramework.Compilation.JavaScriptBuildProvider).AssemblyQualifiedName, ".jsx");
            }
            else
            {
                RegisterIntegratedPipelineBuildProvider(typeof(AjaxControlFramework.Compilation.JavaScriptBuildProvider).AssemblyQualifiedName, ".jsx");
            }
        }


        private static void RegisterAssembliesDeclaration()
        {
            if (!HttpRuntime.UsingIntegratedPipeline)
            {
                RegisterNonIntegratedPipelineAssembly("AjaxControlFramework, Version=1.0.1.0, Culture=neutral, PublicKeyToken=null");
            }
            else
            {
                RegisterIntegratedPipelineAssembly("AjaxControlFramework, Version=1.0.1.0, Culture=neutral, PublicKeyToken=null");
            }
        }


        private static void RegisterNamespaceDeclaration()
        {
            if (!HttpRuntime.UsingIntegratedPipeline)
            {
                RegisterNonIntegratedPipelineNamespace("AjaxControlFramework");
            }
            else
            {
                RegisterIntegratedPipelineNamespace("AjaxControlFramework");
            }
        }


        private static void RegisterControlDeclaration()
        {
            if (!HttpRuntime.UsingIntegratedPipeline)
            {
                RegisterNonIntegratedPipelineControl("ajaxControl", "AjaxControlFramework", "AjaxControlFramework, Version=1.0.1.0, Culture=neutral, PublicKeyToken=null");
            }
            else
            {
                RegisterIntegratedPipelineControl("ajaxControl", "AjaxControlFramework", "AjaxControlFramework, Version=1.0.1.0, Culture=neutral, PublicKeyToken=null");
            }
        }
        //------\\ Methods //-----------------------------------------------//


        
        // TODO: detect an assembly-level attribute that disables the auto-installation of the AJAX Control Framework to the web.config file.




        //------// Non-Integrated Pipeline Methods \\-----------------------\\
        private static void RegisterNonIntegratedPipelineHttpHandler(string handlerType, string verb, string path)
        {
            foreach (HttpHandlersSection handlers in NonIntegratedPipelineHttpHandlers)
            {
                foreach (HttpHandlerAction handler in handlers.Handlers)
                {
                    if (path == handler.Path)
                    {
                        break;
                    }
                }

                handlers.Handlers.Add(new HttpHandlerAction(path, handlerType, verb));
                WebConfigModified = true;
            }
        }


        private static void RegisterNonIntegratedPipelineBuildProvider(string buildProviderType, string extension)
        {
            foreach (BuildProviderCollection buildProviders in NonIntegratedPipelineBuildProviders)
            {
                foreach (BuildProvider buildProvider in buildProviders)
                {
                    if (extension == buildProvider.Extension)
                    {
                        break;
                    }
                }

                buildProviders.Add(new BuildProvider(extension, buildProviderType));
                WebConfigModified = true;
            }
        }


        private static void RegisterNonIntegratedPipelineAssembly(string fullyQualifiedAssemblyName)
        {
            foreach (AssemblyCollection assemblies in NonIntegratedPipelineAssemblies)
            {
                foreach (AssemblyInfo assembly in assemblies)
                {
                    if (fullyQualifiedAssemblyName == assembly.Assembly)
                    {
                        break;
                    }
                }

                assemblies.Add(new AssemblyInfo(fullyQualifiedAssemblyName));
                WebConfigModified = true;
            }
        }


        private static void RegisterNonIntegratedPipelineNamespace(string namespaceName)
        {
            foreach (NamespaceCollection namespaces in NonIntegratedPipelineNamespaces)
            {
                foreach (NamespaceInfo namespaceInfo in namespaces)
                {
                    if (namespaceName == namespaceInfo.Namespace)
                    {
                        break;
                    }
                }

                namespaces.Add(new NamespaceInfo(namespaceName));
                WebConfigModified = true;
            }
        }


        private static void RegisterNonIntegratedPipelineControl(string tagPrefix, string namespaceName, string assembly)
        {
            foreach (TagPrefixCollection controls in NonIntegratedPipelineControls)
            {
                foreach (TagPrefixInfo control in controls)
                {
                    if (tagPrefix == control.TagPrefix && namespaceName == control.Namespace && assembly == control.Assembly)
                    {
                        break;
                    }
                }

                controls.Add(new TagPrefixInfo(tagPrefix, namespaceName, assembly, String.Empty, String.Empty));
                WebConfigModified = true;
            }
        }
        //------\\ Non-Integrated Pipeline Methods //-----------------------//



        //------// Integrated Pipeline Methods \\---------------------------\\
        private static bool IntegratedPipelineHttpHandlerExists(string name)
        {
            return (WebConfigDocument.SelectSingleNode("/configuration/system.webServer/handlers/add[@name='" + name + "']") != null);
        }


        private static void RegisterIntegratedPipelineHttpHandler(string handlerType, string name, string verb, string path)
        {
            XmlNode webServerNode = null;

            if (IntegratedPipelineLocations.Count > 0)
            {
                foreach (List<XmlNode> locations in IntegratedPipelineLocations.Values)
                {
                    foreach (XmlNode location in locations)
                    {
                        if ((webServerNode = location.SelectSingleNode("system.webServer")) != null)
                        {
                            break;
                        }
                    }


                    if (webServerNode == null)
                    {
                        // Fallback to the default, non-location logic.
                        break;
                    }
                    else
                    {
                        XmlNode handlersNode = webServerNode.SelectSingleNode("handlers");

                        if (handlersNode == null)
                        {
                            handlersNode = WebConfigDocument.CreateElement("handlers");
                            webServerNode.AppendChild(handlersNode);

                            handlersNode.AppendChild(CreateIntegratedPipelineHandlerAddElement(handlerType, name, verb, path));
                            WebConfigModified = true;
                        }
                        else
                        {
                            XmlNode addHandlerNode = handlersNode.SelectSingleNode("add[@name='" + name + "']");

                            if (addHandlerNode == null)
                            {
                                handlersNode.AppendChild(CreateIntegratedPipelineHandlerAddElement(handlerType, name, verb, path));
                                WebConfigModified = true;
                            }
                            else
                            {
                                XmlAttribute handlerTypeAttribute = addHandlerNode.Attributes["type"];

                                if (handlerTypeAttribute == null || !String.Equals(handlerTypeAttribute.Value, handlerType))
                                {
                                    ((XmlElement)addHandlerNode).SetAttribute("type", handlerType);
                                    WebConfigModified = true;
                                }


                                XmlAttribute verbAttribute = addHandlerNode.Attributes["verb"];

                                if (verbAttribute == null || !String.Equals(verbAttribute.Value, verb))
                                {
                                    ((XmlElement)addHandlerNode).SetAttribute("verb", verb);
                                    WebConfigModified = true;
                                }


                                XmlAttribute pathAttribute = addHandlerNode.Attributes["path"];

                                if (pathAttribute == null || !String.Equals(pathAttribute.Value, path))
                                {
                                    ((XmlElement)addHandlerNode).SetAttribute("path", path);
                                    WebConfigModified = true;
                                }
                            }
                        }
                    }
                }
            }

            
            if (IntegratedPipelineLocations.Count == 0 || webServerNode == null)
            {
                if (IntegratedPipelineHttpHandlerExists(name))
                {
                    return;
                }


                webServerNode = WebConfigDocument.SelectSingleNode("/configuration/system.webServer");

                if (webServerNode == null)
                {
                    webServerNode = WebConfigDocument.CreateElement("system.webServer");

                    XmlNode configurationNode = WebConfigDocument.SelectSingleNode("//configuration");
                    configurationNode.AppendChild(webServerNode);
                }


                XmlNode handlersNode = webServerNode.SelectSingleNode("/configuration/system.webServer/handlers");

                if (handlersNode == null)
                {
                    handlersNode = WebConfigDocument.CreateElement("handlers");
                    webServerNode.AppendChild(handlersNode);
                }


                handlersNode.AppendChild(CreateIntegratedPipelineHandlerAddElement(handlerType, name, verb, path));
                WebConfigModified = true;
            }
        }


        private static XmlElement CreateIntegratedPipelineHandlerAddElement(string handlerType, string name, string verb, string path)
        {
            XmlElement addHandlerElement = WebConfigDocument.CreateElement("add");

            addHandlerElement.SetAttribute("name", name);
            addHandlerElement.SetAttribute("type", handlerType);
            addHandlerElement.SetAttribute("path", path);
            addHandlerElement.SetAttribute("verb", verb);

            return addHandlerElement;
        }


        private static bool IntegratedPipelineBuildProviderExists(string extension)
        {
            return (WebConfigDocument.SelectSingleNode("/configuration/system.web/compilation/buildProviders/add[@extension='" + extension + "']") != null);
        }


        private static void RegisterIntegratedPipelineBuildProvider(string buildProviderType, string extension)
        {
            XmlNode webNode = null;

            if (IntegratedPipelineLocations.Count > 0)
            {
                foreach (List<XmlNode> locations in IntegratedPipelineLocations.Values)
                {
                    foreach (XmlNode location in locations)
                    {
                        if ((webNode = location.SelectSingleNode("system.web")) != null)
                        {
                            break;
                        }
                    }


                    if (webNode == null)
                    {
                        // Fallback to the default, non-location logic.
                        break;
                    }
                    else
                    {
                        XmlNode compilationNode = webNode.SelectSingleNode("compilation");

                        if (compilationNode == null)
                        {
                            // Fallback to the default, non-location logic.
                            webNode = null;
                            break;
                        }
                        else
                        {
                            XmlNode buildProvidersNode = compilationNode.SelectSingleNode("buildProviders");

                            if (buildProvidersNode == null)
                            {
                                buildProvidersNode = WebConfigDocument.CreateElement("buildProviders");
                                compilationNode.AppendChild(buildProvidersNode);

                                WebConfigModified = true;
                            }



                            XmlNode addBuildProviderNode = buildProvidersNode.SelectSingleNode("add[@extension='" + extension + "']");

                            if (addBuildProviderNode == null)
                            {
                                buildProvidersNode.AppendChild(CreateIntegratedPipelineBuildProviderAddElement(buildProviderType, extension));
                                WebConfigModified = true;
                            }
                            else
                            {
                                XmlAttribute buildProviderTypeAttribute = addBuildProviderNode.Attributes["type"];

                                if (buildProviderTypeAttribute == null || !String.Equals(buildProviderTypeAttribute.Value, buildProviderType))
                                {
                                    ((XmlElement)addBuildProviderNode).SetAttribute("type", buildProviderType);
                                    WebConfigModified = true;
                                }
                            }
                        }
                    }
                }
            }


            if (IntegratedPipelineLocations.Count == 0 || webNode == null)
            {
                if (IntegratedPipelineBuildProviderExists(extension))
                {
                    return;
                }


                webNode = WebConfigDocument.SelectSingleNode("/configuration/system.web");

                if (webNode == null)
                {
                    return;
                }


                XmlNode compilationNode = webNode.SelectSingleNode("/configuration/system.web/compilation");

                if (compilationNode == null)
                {
                    return;
                }


                XmlNode buildProvidersNode = webNode.SelectSingleNode("/configuration/system.web/compilation/buildProviders");

                if (buildProvidersNode == null)
                {
                    buildProvidersNode = WebConfigDocument.CreateElement("buildProviders");
                    compilationNode.AppendChild(buildProvidersNode);
                }
                

                buildProvidersNode.AppendChild(CreateIntegratedPipelineBuildProviderAddElement(buildProviderType, extension));
                WebConfigModified = true;
            }
        }


        private static XmlElement CreateIntegratedPipelineBuildProviderAddElement(string buildProviderType, string extension)
        {
            XmlElement addBuildProviderElement = WebConfigDocument.CreateElement("add");

            addBuildProviderElement.SetAttribute("type", buildProviderType);
            addBuildProviderElement.SetAttribute("extension", extension);

            return addBuildProviderElement;
        }


        private static bool IntegratedPipelineAssemblyExists(string fullyQualifiedAssemblyName)
        {
            return (WebConfigDocument.SelectSingleNode("/configuration/system.web/compilation/assemblies/add[@assembly='" + fullyQualifiedAssemblyName + "']") != null);
        }


        private static void RegisterIntegratedPipelineAssembly(string fullyQualifiedAssemblyName)
        {
            XmlNode webNode = null;

            if (IntegratedPipelineLocations.Count > 0)
            {
                foreach (List<XmlNode> locations in IntegratedPipelineLocations.Values)
                {
                    foreach (XmlNode location in locations)
                    {
                        if ((webNode = location.SelectSingleNode("system.web")) != null)
                        {
                            break;
                        }
                    }


                    if (webNode == null)
                    {
                        // Fallback to the default, non-location logic.
                        break;
                    }
                    else
                    {
                        XmlNode compilationNode = webNode.SelectSingleNode("compilation");

                        if (compilationNode == null)
                        {
                            // Fallback to the default, non-location logic.
                            webNode = null;
                            break;
                        }
                        else
                        {
                            XmlNode assembliesNode = compilationNode.SelectSingleNode("assemblies");

                            if (assembliesNode == null)
                            {
                                assembliesNode = WebConfigDocument.CreateElement("assemblies");
                                compilationNode.AppendChild(assembliesNode);

                                WebConfigModified = true;
                            }



                            XmlNode addAssemblyNode = assembliesNode.SelectSingleNode("add[@assembly='" + fullyQualifiedAssemblyName + "']");

                            if (addAssemblyNode == null)
                            {
                                assembliesNode.AppendChild(CreateIntegratedPipelineAssemblyAddElement(fullyQualifiedAssemblyName));
                                WebConfigModified = true;
                            }
                            else
                            {
                                XmlAttribute assemblyAttribute = addAssemblyNode.Attributes["assembly"];

                                if (assemblyAttribute == null || !String.Equals(assemblyAttribute.Value, fullyQualifiedAssemblyName))
                                {
                                    ((XmlElement)addAssemblyNode).SetAttribute("assembly", fullyQualifiedAssemblyName);
                                    WebConfigModified = true;
                                }
                            }
                        }
                    }
                }
            }


            if (IntegratedPipelineLocations.Count == 0 || webNode == null)
            {
                if (IntegratedPipelineAssemblyExists(fullyQualifiedAssemblyName))
                {
                    return;
                }


                webNode = WebConfigDocument.SelectSingleNode("/configuration/system.web");

                if (webNode == null)
                {
                    return;
                }


                XmlNode compilationNode = webNode.SelectSingleNode("/configuration/system.web/compilation");

                if (compilationNode == null)
                {
                    return;
                }


                XmlNode assembliesNode = webNode.SelectSingleNode("/configuration/system.web/compilation/assemblies");

                if (assembliesNode == null)
                {
                    assembliesNode = WebConfigDocument.CreateElement("assemblies");
                    compilationNode.AppendChild(assembliesNode);
                }


                assembliesNode.AppendChild(CreateIntegratedPipelineAssemblyAddElement(fullyQualifiedAssemblyName));
                WebConfigModified = true;
            }
        }


        private static XmlElement CreateIntegratedPipelineAssemblyAddElement(string fullyQualifiedAssemblyName)
        {
            XmlElement addAssemblyElement = WebConfigDocument.CreateElement("add");

            addAssemblyElement.SetAttribute("assembly", fullyQualifiedAssemblyName);

            return addAssemblyElement;
        }


        private static bool IntegratedPipelineNamespaceExists(string namespaceName)
        {
            return (WebConfigDocument.SelectSingleNode("/configuration/system.web/pages/namespaces/add[@namespace='" + namespaceName + "']") != null);
        }


        private static void RegisterIntegratedPipelineNamespace(string namespaceName)
        {
            XmlNode webNode = null;

            if (IntegratedPipelineLocations.Count > 0)
            {
                foreach (List<XmlNode> locations in IntegratedPipelineLocations.Values)
                {
                    foreach (XmlNode location in locations)
                    {
                        if ((webNode = location.SelectSingleNode("system.web")) != null)
                        {
                            break;
                        }
                    }


                    if (webNode == null)
                    {
                        // Fallback to the default, non-location logic.
                        break;
                    }
                    else
                    {
                        XmlNode pagesNode = webNode.SelectSingleNode("pages");

                        if (pagesNode == null)
                        {
                            pagesNode = WebConfigDocument.CreateElement("pages");
                            webNode.AppendChild(pagesNode);

                            WebConfigModified = true;
                        }
                        else
                        {
                            XmlNode namespacesNode = pagesNode.SelectSingleNode("namespaces");

                            if (namespacesNode == null)
                            {
                                namespacesNode = WebConfigDocument.CreateElement("namespaces");
                                pagesNode.AppendChild(namespacesNode);

                                WebConfigModified = true;
                            }


                            XmlNode addNamespaceNode = namespacesNode.SelectSingleNode("add[@namespace='" + namespaceName + "']");

                            if (addNamespaceNode == null)
                            {
                                namespacesNode.AppendChild(CreateIntegratedPipelineNamespaceAddElement(namespaceName));
                                WebConfigModified = true;
                            }
                            else
                            {
                                XmlAttribute namespaceAttribute = addNamespaceNode.Attributes["namespace"];

                                if (namespaceAttribute == null || !String.Equals(namespaceAttribute.Value, namespaceName))
                                {
                                    ((XmlElement)addNamespaceNode).SetAttribute("namespace", namespaceName);
                                    WebConfigModified = true;
                                }
                            }
                        }
                    }
                }
            }


            if (IntegratedPipelineLocations.Count == 0 || webNode == null)
            {
                if (IntegratedPipelineNamespaceExists(namespaceName))
                {
                    return;
                }


                webNode = WebConfigDocument.SelectSingleNode("/configuration/system.web");

                if (webNode == null)
                {
                    return;
                }


                XmlNode pagesNode = webNode.SelectSingleNode("/configuration/system.web/pages");

                if (pagesNode == null)
                {
                    pagesNode = WebConfigDocument.CreateElement("pages");
                    webNode.AppendChild(pagesNode);
                }


                XmlNode namespacesNode = webNode.SelectSingleNode("/configuration/system.web/compilation/namespaces");

                if (namespacesNode == null)
                {
                    namespacesNode = WebConfigDocument.CreateElement("namespaces");
                    pagesNode.AppendChild(namespacesNode);
                }


                namespacesNode.AppendChild(CreateIntegratedPipelineNamespaceAddElement(namespaceName));
                WebConfigModified = true;
            }
        }


        private static XmlElement CreateIntegratedPipelineNamespaceAddElement(string namespaceName)
        {
            XmlElement addNamespaceElement = WebConfigDocument.CreateElement("add");

            addNamespaceElement.SetAttribute("namespace", namespaceName);

            return addNamespaceElement;
        }


        private static bool IntegratedPipelineControlExists(string tagPrefix, string namespaceName, string assembly)
        {
            return (WebConfigDocument.SelectSingleNode("/configuration/system.web/pages/controls/add[@tagPrefix='" + tagPrefix + "' and @namespace='" + namespaceName + "' and @assembly='" + assembly + "']") != null);
        }


        private static void RegisterIntegratedPipelineControl(string tagPrefix, string namespaceName, string assembly)
        {
            XmlNode webNode = null;

            if (IntegratedPipelineLocations.Count > 0)
            {
                foreach (List<XmlNode> locations in IntegratedPipelineLocations.Values)
                {
                    foreach (XmlNode location in locations)
                    {
                        if ((webNode = location.SelectSingleNode("system.web")) != null)
                        {
                            break;
                        }
                    }


                    if (webNode == null)
                    {
                        // Fallback to the default, non-location logic.
                        break;
                    }
                    else
                    {
                        XmlNode pagesNode = webNode.SelectSingleNode("pages");

                        if (pagesNode == null)
                        {
                            pagesNode = WebConfigDocument.CreateElement("pages");
                            webNode.AppendChild(pagesNode);

                            WebConfigModified = true;
                        }


                        XmlNode controlsNode = pagesNode.SelectSingleNode("controls");

                        if (controlsNode == null)
                        {
                            controlsNode = WebConfigDocument.CreateElement("controls");
                            pagesNode.AppendChild(controlsNode);

                            WebConfigModified = true;
                        }


                        XmlNode addControlNode = controlsNode.SelectSingleNode("add[@tagPrefix='" + tagPrefix + "' and @namespace='" + namespaceName + "' and @assembly='" + assembly + "']");

                        if (addControlNode == null)
                        {
                            controlsNode.AppendChild(CreateIntegratedPipelineControlAddElement(tagPrefix, namespaceName, assembly));
                            WebConfigModified = true;
                        }
                        else
                        {
                            XmlAttribute tagPrefixAttribute = addControlNode.Attributes["tagPrefix"];

                            if (tagPrefixAttribute == null || !String.Equals(tagPrefixAttribute.Value, tagPrefix))
                            {
                                ((XmlElement)addControlNode).SetAttribute("tagPrefix", tagPrefix);
                                WebConfigModified = true;
                            }


                            XmlAttribute namespaceAttribute = addControlNode.Attributes["namespace"];

                            if (namespaceAttribute == null || !String.Equals(namespaceAttribute.Value, namespaceName))
                            {
                                ((XmlElement)addControlNode).SetAttribute("namespace", namespaceName);
                                WebConfigModified = true;
                            }


                            XmlAttribute assemblyAttribute = addControlNode.Attributes["assembly"];

                            if (assemblyAttribute == null || !String.Equals(assemblyAttribute.Value, assembly))
                            {
                                ((XmlElement)addControlNode).SetAttribute("assembly", assembly);
                                WebConfigModified = true;
                            }
                        }
                    }
                }
            }


            if (IntegratedPipelineLocations.Count == 0 || webNode == null)
            {
                if (IntegratedPipelineControlExists(tagPrefix, namespaceName, assembly))
                {
                    return;
                }


                webNode = WebConfigDocument.SelectSingleNode("/configuration/system.web");

                if (webNode == null)
                {
                    return;
                }


                XmlNode pagesNode = webNode.SelectSingleNode("/configuration/system.web/pages");

                if (pagesNode == null)
                {
                    pagesNode = WebConfigDocument.CreateElement("pages");
                    webNode.AppendChild(pagesNode);
                }


                XmlNode controlsNode = webNode.SelectSingleNode("/configuration/system.web/pages/controls");

                if (controlsNode == null)
                {
                    controlsNode = WebConfigDocument.CreateElement("controls");
                    pagesNode.AppendChild(controlsNode);
                }


                controlsNode.AppendChild(CreateIntegratedPipelineControlAddElement(tagPrefix, namespaceName, assembly));
                WebConfigModified = true;
            }
        }


        private static XmlElement CreateIntegratedPipelineControlAddElement(string tagPrefix, string namespaceName, string assembly)
        {
            XmlElement addControlElement = WebConfigDocument.CreateElement("add");

            addControlElement.SetAttribute("tagPrefix", tagPrefix);
            addControlElement.SetAttribute("namespace", namespaceName);
            addControlElement.SetAttribute("assembly", assembly);

            return addControlElement;
        }
        //------\\ Integrated Pipeline Methods //---------------------------//
    }
}